﻿
using Xacml.Core;
using Xacml.Core.Policy;
using System.Collections;
using System.Collections.Specialized;

namespace Xacml.Core.Runtime
{
  public class Rule : IMatchEvaluable
  {
    private StringCollection _allResources = new StringCollection();
    private Decision _evaluationValue;
    private RuleElement _rule;
    private Condition _condition;
    private Target _target;

    public RuleElement RuleDefinition
    {
      get
      {
        return this._rule;
      }
    }

    public StringCollection AllResources
    {
      get
      {
        return this._allResources;
      }
    }

    public Rule(RuleElement rule)
    {
      this._rule = rule;
      if (this._rule.SchemaVersion == XacmlVersion.Version10 || this._rule.SchemaVersion == XacmlVersion.Version11)
        this._condition = new Condition((ConditionElement) this._rule.Condition);
      else if (this._rule.SchemaVersion == XacmlVersion.Version20)
        this._condition = (Condition) new Condition2((ConditionElement) this._rule.Condition);
      if (rule.Target == null)
        return;
      this._target = new Target((TargetElement) rule.Target);
      foreach (TargetItemBaseReadWrite itemBaseReadWrite in (CollectionBase) rule.Target.Resources.ItemsList)
      {
        foreach (ResourceMatchElement resourceMatchElement in (CollectionBase) itemBaseReadWrite.Match)
        {
          if (!this._allResources.Contains(resourceMatchElement.AttributeValue.Contents))
            this._allResources.Add(resourceMatchElement.AttributeValue.Contents);
        }
      }
    }

    public TargetEvaluationValue Match(EvaluationContext context)
    {
      TargetEvaluationValue targetEvaluationValue = TargetEvaluationValue.Indeterminate;
      context.Trace("Evaluating rule target");
      context.AddIndent();
      try
      {
        targetEvaluationValue = TargetEvaluationValue.Match;
        if (this._target != null)
        {
          targetEvaluationValue = this._target.Evaluate(context);
          context.TraceContextValues();
        }
        return targetEvaluationValue;
      }
      finally
      {
        context.RemoveIndent();
        context.Trace("Target: {0}", (object) targetEvaluationValue);
      }
    }

    public Decision Evaluate(EvaluationContext context)
    {
      context.Trace("Evaluating rule: {0}", (object) this._rule.Description);
      context.AddIndent();
      context.CurrentRule = this;
      try
      {
        TargetEvaluationValue targetEvaluationValue = this.Match(context);
        EvaluationValue evaluationValue = EvaluationValue.True;
        if (this._rule.HasCondition)
          evaluationValue = this._condition.Evaluate(context);
        else
          context.Trace("Rule does not have a condition");
        if (targetEvaluationValue == TargetEvaluationValue.Indeterminate || evaluationValue.IsIndeterminate)
          this._evaluationValue = Decision.Indeterminate;
        else if (targetEvaluationValue == TargetEvaluationValue.Match && evaluationValue.BoolValue)
          this._evaluationValue = this._rule.Effect == Effect.Permit ? Decision.Permit : Decision.Deny;
        else if (targetEvaluationValue == TargetEvaluationValue.NoMatch || targetEvaluationValue == TargetEvaluationValue.Match && !evaluationValue.BoolValue)
          this._evaluationValue = Decision.NotApplicable;
        context.Trace("Rule: {0}", (object) this._evaluationValue);
        return this._evaluationValue;
      }
      finally
      {
        context.RemoveIndent();
        context.CurrentRule = (Rule) null;
      }
    }
  }
}
